{
 "cells": [
  {
   "cell_type": "code",
   "id": "initial_id",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "is_executing": true
    },
    "ExecuteTime": {
     "start_time": "2025-10-11T09:27:36.081897Z"
    }
   },
   "source": [
    "# cifar10_model.py\n",
    "import tensorflow as tf\n",
    "from tensorflow.keras import layers, models\n",
    "import numpy as np\n",
    "\n",
    "# 1. 加载CIFAR-10数据集\n",
    "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()\n",
    "\n",
    "# 数据预处理\n",
    "x_train = x_train.astype('float32') / 255.0  # 归一化到[0,1]\n",
    "x_test = x_test.astype('float32') / 255.0\n",
    "y_train = tf.keras.utils.to_categorical(y_train, 10)  # 独热编码\n",
    "y_test = tf.keras.utils.to_categorical(y_test, 10)\n",
    "\n",
    "# 2. 搭建CNN模型（简化版残差结构）\n",
    "def build_cnn_model():\n",
    "    model = models.Sequential([\n",
    "        # 第一个卷积块\n",
    "        layers.Conv2D(32, (3, 3), padding='same', input_shape=(32, 32, 3)),\n",
    "        layers.BatchNormalization(),\n",
    "        layers.Activation('relu'),\n",
    "        layers.Conv2D(32, (3, 3), padding='same'),\n",
    "        layers.BatchNormalization(),\n",
    "        layers.Activation('relu'),\n",
    "        layers.MaxPooling2D((2, 2)),\n",
    "        layers.Dropout(0.2),\n",
    "\n",
    "        # 第二个卷积块\n",
    "        layers.Conv2D(64, (3, 3), padding='same'),\n",
    "        layers.BatchNormalization(),\n",
    "        layers.Activation('relu'),\n",
    "        layers.Conv2D(64, (3, 3), padding='same'),\n",
    "        layers.BatchNormalization(),\n",
    "        layers.Activation('relu'),\n",
    "        layers.MaxPooling2D((2, 2)),\n",
    "        layers.Dropout(0.3),\n",
    "\n",
    "        # 第三个卷积块\n",
    "        layers.Conv2D(128, (3, 3), padding='same'),\n",
    "        layers.BatchNormalization(),\n",
    "        layers.Activation('relu'),\n",
    "        layers.Conv2D(128, (3, 3), padding='same'),\n",
    "        layers.BatchNormalization(),\n",
    "        layers.Activation('relu'),\n",
    "        layers.MaxPooling2D((2, 2)),\n",
    "        layers.Dropout(0.4),\n",
    "\n",
    "        # 分类头\n",
    "        layers.Flatten(),\n",
    "        layers.Dense(128, activation='relu'),\n",
    "        layers.BatchNormalization(),\n",
    "        layers.Dropout(0.5),\n",
    "        layers.Dense(10, activation='softmax')\n",
    "    ])\n",
    "    return model\n",
    "\n",
    "# 3. 训练模型\n",
    "model = build_cnn_model()\n",
    "model.compile(\n",
    "    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),\n",
    "    loss='categorical_crossentropy',\n",
    "    metrics=['accuracy']\n",
    ")\n",
    "\n",
    "# 训练（可根据需要调整epochs）\n",
    "history = model.fit(\n",
    "    x_train, y_train,\n",
    "    batch_size=64,\n",
    "    epochs=30,\n",
    "    validation_split=0.1,\n",
    "    verbose=1\n",
    ")\n",
    "\n",
    "# 评估模型\n",
    "test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)\n",
    "print(f\"测试集准确率: {test_acc:.4f}\")\n",
    "\n",
    "# 保存模型（供Flask应用使用）\n",
    "model.save('cifar10_cnn_model.h5')\n",
    "print(\"模型已保存为 'cifar10_cnn_model.h5'\")\n",
    "\n",
    "# CIFAR-10类别名称\n",
    "class_names = [\n",
    "    '飞机', '汽车', '鸟', '猫', '鹿',\n",
    "    '狗', '青蛙', '马', '船', '卡车'\n",
    "]"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/30\n",
      " 94/704 [===>..........................] - ETA: 5:18 - loss: 2.4192 - accuracy: 0.2362"
     ]
    }
   ],
   "execution_count": null
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T09:27:31.737954Z",
     "start_time": "2025-10-11T09:27:29.811952Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# app.py\n",
    "from flask import Flask, request, jsonify, render_template\n",
    "from flask_cors import CORS\n",
    "import tensorflow as tf\n",
    "import numpy as np\n",
    "from PIL import Image\n",
    "import io\n",
    "import os\n",
    "\n",
    "app = Flask(__name__)\n",
    "CORS(app)  # 解决跨域问题\n",
    "\n",
    "# 加载训练好的模型和类别名称\n",
    "model = tf.keras.models.load_model('cifar10_cnn_model.h5')\n",
    "class_names = [\n",
    "    '飞机', '汽车', '鸟', '猫', '鹿',\n",
    "    '狗', '青蛙', '马', '船', '卡车'\n",
    "]\n",
    "\n",
    "# 图片预处理（适配CIFAR-10输入格式）\n",
    "def preprocess_image(image):\n",
    "    # 调整尺寸为32×32（CIFAR-10输入大小）\n",
    "    image = image.resize((32, 32))\n",
    "    # 转换为数组并归一化\n",
    "    img_array = np.array(image).astype('float32') / 255.0\n",
    "    # 增加批次维度（模型输入需要(batch_size, 32, 32, 3)）\n",
    "    return np.expand_dims(img_array, axis=0)\n",
    "\n",
    "# 首页路由（返回上传界面）\n",
    "@app.route('/')\n",
    "def index():\n",
    "    return render_template('index.html')\n",
    "\n",
    "# 预测接口（接收图片，返回结果）\n",
    "@app.route('/predict', methods=['POST'])\n",
    "def predict():\n",
    "    if 'file' not in request.files:\n",
    "        return jsonify({'error': '未上传图片'}), 400\n",
    "\n",
    "    file = request.files['file']\n",
    "    if file.filename == '':\n",
    "        return jsonify({'error': '未选择图片'}), 400\n",
    "\n",
    "    try:\n",
    "        # 读取图片并预处理\n",
    "        image = Image.open(io.BytesIO(file.read()))\n",
    "        # 确保图片是RGB格式（处理PNG等带alpha通道的图片）\n",
    "        if image.mode != 'RGB':\n",
    "            image = image.convert('RGB')\n",
    "        processed_img = preprocess_image(image)\n",
    "\n",
    "        # 模型预测\n",
    "        predictions = model.predict(processed_img)[0]  # 取第一个样本的预测结果\n",
    "        predicted_class_idx = np.argmax(predictions)\n",
    "        predicted_class = class_names[predicted_class_idx]\n",
    "        confidence = float(predictions[predicted_class_idx])\n",
    "\n",
    "        # 整理所有类别的概率（用于前端展示）\n",
    "        class_probabilities = {\n",
    "            class_names[i]: float(predictions[i]) for i in range(10)\n",
    "        }\n",
    "\n",
    "        return jsonify({\n",
    "            'predicted_class': predicted_class,\n",
    "            'confidence': confidence,\n",
    "            'all_probabilities': class_probabilities\n",
    "        })\n",
    "\n",
    "    except Exception as e:\n",
    "        return jsonify({'error': str(e)}), 500\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # 创建uploads文件夹（如果需要保存上传的图片）\n",
    "    if not os.path.exists('uploads'):\n",
    "        os.makedirs('uploads')\n",
    "    app.run(debug=True)  # 生产环境需关闭debug，设置host='0.0.0.0'"
   ],
   "id": "2756fabbd100299f",
   "outputs": [
    {
     "ename": "OSError",
     "evalue": "No file or directory found at cifar10_cnn_model.h5",
     "output_type": "error",
     "traceback": [
      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[1;31mOSError\u001B[0m                                   Traceback (most recent call last)",
      "Cell \u001B[1;32mIn[3], line 14\u001B[0m\n\u001B[0;32m     11\u001B[0m CORS(app)  \u001B[38;5;66;03m# 解决跨域问题\u001B[39;00m\n\u001B[0;32m     13\u001B[0m \u001B[38;5;66;03m# 加载训练好的模型和类别名称\u001B[39;00m\n\u001B[1;32m---> 14\u001B[0m model \u001B[38;5;241m=\u001B[39m \u001B[43mtf\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mkeras\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mmodels\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mload_model\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43mcifar10_cnn_model.h5\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m)\u001B[49m\n\u001B[0;32m     15\u001B[0m class_names \u001B[38;5;241m=\u001B[39m [\n\u001B[0;32m     16\u001B[0m     \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m飞机\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m汽车\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m鸟\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m猫\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m鹿\u001B[39m\u001B[38;5;124m'\u001B[39m,\n\u001B[0;32m     17\u001B[0m     \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m狗\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m青蛙\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m马\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m船\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m卡车\u001B[39m\u001B[38;5;124m'\u001B[39m\n\u001B[0;32m     18\u001B[0m ]\n\u001B[0;32m     20\u001B[0m \u001B[38;5;66;03m# 图片预处理（适配CIFAR-10输入格式）\u001B[39;00m\n",
      "File \u001B[1;32mD:\\anaconda\\envs\\homl3\\lib\\site-packages\\keras\\src\\saving\\saving_api.py:262\u001B[0m, in \u001B[0;36mload_model\u001B[1;34m(filepath, custom_objects, compile, safe_mode, **kwargs)\u001B[0m\n\u001B[0;32m    254\u001B[0m     \u001B[38;5;28;01mreturn\u001B[39;00m saving_lib\u001B[38;5;241m.\u001B[39mload_model(\n\u001B[0;32m    255\u001B[0m         filepath,\n\u001B[0;32m    256\u001B[0m         custom_objects\u001B[38;5;241m=\u001B[39mcustom_objects,\n\u001B[0;32m    257\u001B[0m         \u001B[38;5;28mcompile\u001B[39m\u001B[38;5;241m=\u001B[39m\u001B[38;5;28mcompile\u001B[39m,\n\u001B[0;32m    258\u001B[0m         safe_mode\u001B[38;5;241m=\u001B[39msafe_mode,\n\u001B[0;32m    259\u001B[0m     )\n\u001B[0;32m    261\u001B[0m \u001B[38;5;66;03m# Legacy case.\u001B[39;00m\n\u001B[1;32m--> 262\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m legacy_sm_saving_lib\u001B[38;5;241m.\u001B[39mload_model(\n\u001B[0;32m    263\u001B[0m     filepath, custom_objects\u001B[38;5;241m=\u001B[39mcustom_objects, \u001B[38;5;28mcompile\u001B[39m\u001B[38;5;241m=\u001B[39m\u001B[38;5;28mcompile\u001B[39m, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs\n\u001B[0;32m    264\u001B[0m )\n",
      "File \u001B[1;32mD:\\anaconda\\envs\\homl3\\lib\\site-packages\\keras\\src\\utils\\traceback_utils.py:70\u001B[0m, in \u001B[0;36mfilter_traceback.<locals>.error_handler\u001B[1;34m(*args, **kwargs)\u001B[0m\n\u001B[0;32m     67\u001B[0m     filtered_tb \u001B[38;5;241m=\u001B[39m _process_traceback_frames(e\u001B[38;5;241m.\u001B[39m__traceback__)\n\u001B[0;32m     68\u001B[0m     \u001B[38;5;66;03m# To get the full stack trace, call:\u001B[39;00m\n\u001B[0;32m     69\u001B[0m     \u001B[38;5;66;03m# `tf.debugging.disable_traceback_filtering()`\u001B[39;00m\n\u001B[1;32m---> 70\u001B[0m     \u001B[38;5;28;01mraise\u001B[39;00m e\u001B[38;5;241m.\u001B[39mwith_traceback(filtered_tb) \u001B[38;5;28;01mfrom\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m\n\u001B[0;32m     71\u001B[0m \u001B[38;5;28;01mfinally\u001B[39;00m:\n\u001B[0;32m     72\u001B[0m     \u001B[38;5;28;01mdel\u001B[39;00m filtered_tb\n",
      "File \u001B[1;32mD:\\anaconda\\envs\\homl3\\lib\\site-packages\\keras\\src\\saving\\legacy\\save.py:234\u001B[0m, in \u001B[0;36mload_model\u001B[1;34m(filepath, custom_objects, compile, options)\u001B[0m\n\u001B[0;32m    232\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(filepath_str, \u001B[38;5;28mstr\u001B[39m):\n\u001B[0;32m    233\u001B[0m     \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m tf\u001B[38;5;241m.\u001B[39mio\u001B[38;5;241m.\u001B[39mgfile\u001B[38;5;241m.\u001B[39mexists(filepath_str):\n\u001B[1;32m--> 234\u001B[0m         \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mIOError\u001B[39;00m(\n\u001B[0;32m    235\u001B[0m             \u001B[38;5;124mf\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mNo file or directory found at \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mfilepath_str\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m\"\u001B[39m\n\u001B[0;32m    236\u001B[0m         )\n\u001B[0;32m    238\u001B[0m     \u001B[38;5;28;01mif\u001B[39;00m tf\u001B[38;5;241m.\u001B[39mio\u001B[38;5;241m.\u001B[39mgfile\u001B[38;5;241m.\u001B[39misdir(filepath_str):\n\u001B[0;32m    239\u001B[0m         \u001B[38;5;28;01mreturn\u001B[39;00m saved_model_load\u001B[38;5;241m.\u001B[39mload(\n\u001B[0;32m    240\u001B[0m             filepath_str, \u001B[38;5;28mcompile\u001B[39m, options\n\u001B[0;32m    241\u001B[0m         )\n",
      "\u001B[1;31mOSError\u001B[0m: No file or directory found at cifar10_cnn_model.h5"
     ]
    }
   ],
   "execution_count": 3
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
